JBoss Community Archive (Read Only)

Errai

Errai JAX-RS

JAX-RS (Java API for RESTful Web Services) is a Java EE standard (JSR-311) for implementing REST-based Web services in Java. Errai JAX-RS brings this standard to the browser and simplifies the integration of REST-based services in GWT client applications. Errai can generate proxies based on JAX-RS interfaces which will handle all the underlying communication and serialization logic. All that's left to do is to invoke a Java method.

Errai's JAX-RS support consists of the following:

To get started with a working demo that makes use of Errai JAX-RS you can either download and unzip the Errai tutorial project or check out the CRUD demo part of our demo collection. If you prefer getting started from scratch keep reading images/author/images/icons/emoticons/smile.gif .

Getting Started

Dependencies

To use Errai JAX-RS, you must include it on the compile-time classpath. If you are using Maven for your build, add this dependency:

    <dependency>
      <groupId>org.jboss.errai</groupId>
      <artifactId>errai-jaxrs-client</artifactId>
      <version>${errai.version}</version>
      <scope>provided</scope>
    </dependency>

or if you are not using Maven for dependency management, add errai-jaxrs-client-version.jar to your classpath.

If you intend to use Errai's JSON format on the wire you will need to add Errai's JAX-RS JSON provider to your classpath and make sure it gets deployed to the server.

    <dependency>
      <groupId>org.jboss.errai</groupId>
      <artifactId>errai-jaxrs-provider</artifactId>
      <version>${errai.version}</version>
    </dependency>

or manually add errai-jaxrs-provider-version.jar in case you're not using Maven. If your REST service returns Jackson generated JSON you do not need the errai-jaxrs-provider (see Configuration) .

GWT Module

Once you have Errai JAX-RS on your classpath, ensure your application inherits the GWT module as well. Add this line to your application's *.gwt.xml file:

  <inherits name="org.jboss.errai.enterprise.Jaxrs"/>

Server-Side JAX-RS Implementation

Errai's JAX-RS support consists mostly of features that make the client side easier and more reliable to maintain. You will need to use an existing third-party JAX-RS implementation on the server side. All Java EE 6 application servers include such a module out-of-the-box. If you are developing an application that you intend to deploy on a plain servlet container, you will have to choose a JAX-RS implementation (for example, RestEasy) and configure it properly in your web.xml.

Alternatively, you could keep your REST resource layer in a completely separate web application hosted on the same server (perhaps build an Errai JAX-RS client against an existing REST service you developed previously). In this case, you could factor out the shared JAX-RS interface into a shared library, leaving the implementation in the non-Errai application.

Finally, you can take advantage of the cross-origin resource sharing (CoRS) feature in modern browsers and use Errai JAX-RS to send requests to a third-party server. The third-party server would have to be configured to allow cross-domain requests. In this case, you would write a JAX-RS-Annotated interface describing the remote REST resources, but you would not create an implementation of that interface.

Shared JAX-RS Interface

Errai JAX-RS works by leveraging standard Java interfaces that bear JAX-RS annotations. You will also want these interfaces visible to server-side code so that your JAX-RS resource classes can implement them (and inherit the annotations). This keeps the whole setup typesafe, and reduces duplication to the bare minimum. The natural solution, then is to put the JAX-RS interfaces under the client.shared package within your GWT module:

  • project

    • src

      • main

        • java

          • com.mycompany.myapp

            • MyApp.gwt.xml [the app's GWT module]

          • com.mycompany.myapp.client.local

            • MyAppClientStuff.java [code that @Injects Caller<MyAppRestResource>]

          • com.mycompany.myapp.client.shared

            • CustomerService.java [the JAX-RS interface]

          • com.mycompany.myapp.server

            • CustomerServiceImpl.java [the server-side JAX-RS resource implementation]

The contents of the server-side files would be as follows:

CustomerService.java
@Path("customers")
public interface CustomerService {
  @GET
  @Produces("application/json")
  public List<Customer> listAllCustomers();

  @POST
  @Consumes("application/json")
  @Produces("text/plain")
  public long createCustomer(Customer customer);
}

The above interface is visible both to server-side code and to client-side code. It is used by client-side code to describe the available operations, their parameter types, and their return types. If you use your IDE's refactoring tools to modify this interface, both the server-side and client-side code will be updated automatically.

CustomerServiceImpl.java
public class CustomerServiceImpl implements CustomerService {

  @Override
  public List<Customer> listAllCustomers() {
    // Use a database API to look up all customers in back-end data store
    // Return the resulting list
  }

  @Override
  public long createCustomer(Customer customer) {
    // Store new Customer instance in back-end data store
  }
}

The above class implements the shared interface. Since it performs database and/or filesystem operations to manipulate the persistent data store, it is not GWT translatable, and it's therefore kept in a package that is not part of the GWT module.

Save typing and reduce duplication

Note that all JAX-RS annotations (@Path, @GET, @Consumes, and so on) can be inherited from the interface. You do not need to repeat these annotations in your resource implementation classes.

Creating Requests

This section assumes you have already set up the CustomerService JAX-RS endpoint as described in the previous section.

To create a request on the client, all that needs to be done is to invoke RestClient.create(), thereby providing the JAX-RS interface, a response callback and to invoke the corresponding interface method:

App.java
...
Button create = new Button("Create", new ClickHandler() {
  public void onClick(ClickEvent clickEvent) {
    Customer customer = new Customer(firstName, lastName, postalCode);
    RestClient.create(CustomerService.class, callback).createCustomer(customer);
  }
});
...

For details on the callback mechanism see Handling Responses.

Proxy Injection

Injectable proxies can be used as an alternative to calling RestClient.create().

@Inject
private Caller<CustomerService> customerService; 

To create a request, the callback objects need to be provided to the call method before the corresponding interface method is invoked.

customerService.call(callback).listAllCustomers();

To use caller injection, your application needs to inherit the Errai IOC GWT module. To do this, just add this line to your application's *.gwt.xml file and make sure it comes after the Errai JAX-RS module (see Getting Started):

  <inherits name="org.jboss.errai.ioc.Container"/>

The JAX-RS interfaces need to be visible to the GWT compiler and must therefore reside within the client packages (e.g. client.shared).

Handling Responses

An instance of Errai's RemoteCallback<T> has to be passed to the RestClient.create() call, which will provide access to the JAX-RS resource method's result. T is the return type of the JAX-RS resource method. In the example below it's just a Long representing a customer ID, but it can be any serializable type (see Marshalling).

RemoteCallback<Long> callback = new RemoteCallback<Long>() {
  public void callback(Long id) {
    Window.alert("Customer created with ID: " + id);
  }
};

A special case of this RemoteCallback is the ResponseCallback which can be used as an alternative. It provides access to the Response object representing the underlying HTTP response. This is useful when more details of the HTTP response are needed, such as headers and the status code. The ResponseCallback can also be used for JAX-RS interface methods that return a javax.ws.rs.core.Response type. In this case, the MarshallingWrapper class can be used to manually demarshall the response body to an entity of the desired type.

ResponseCallback callback = new ResponseCallback() {
  public void callback(Response response) {
    Window.alert("HTTP status code: " + response.getStatusCode());
    Window.alert("HTTP response body: " + response.getText());
  }
};

For handling errors, Errai's error callback mechanism can be reused and an instance of ErrorCallback can optionally be passed to the RestClient.create() call. In case of an HTTP error, the ResponseException provides access to the Response object. All other Throwables indicate a communication problem.

ErrorCallback errorCallback = new RestErrorCallback() {
  public boolean error(Request request, Throwable throwable) {
    try {
      throw throwable;
    }
    catch (ResponseException e) {
      Response response = e.getResponse();
      // process unexpected response
      response.getStatusCode();
    }
    catch (Throwable t) {
      // process unexpected error (e.g. a network problem)
    }
    return false;
  }
};

Client-side Interceptors

Client-side remote call interceptors provide the ability to manipulate or bypass the request before it's being sent. This is useful for implementing crosscutting concerns like caching or security features e.g:

  • avoiding the request when the data is cached locally

  • adding special HTTP headers or parameters to the request

To have a JAX-RS remote call intercepted, either an interface method or the remote interface type has to be annotated with @InterceptedCall. If the type is annotated, all interface methods will be intercepted.

@Path("customers")
public interface CustomerService {

  @GET
  @Path("/{id}")
  @Produces("application/json")
  @InterceptedCall(MyCacheInterceptor.class)
  public Customer retrieveCustomerById(@PathParam("id") long id);
}

Note that an ordered list of interceptors can be used for specifying an interceptor chain e.g.

@InterceptedCall({MyCacheInterceptor.class, MySecurityInterceptor.class})
public Customer retrieveCustomerById(@PathParam("id") long id);

Implementing an interceptor is easy:

public class MyCacheInterceptor implements RestClientInterceptor {

  @Override
  public void aroundInvoke(final RestCallContext context) {
    RequestBuilder builder = context.getRequestBuilder();
    builder.setHeader("headerName", "value");
    context.proceed();
  }
}

The RestCallContext passed to the aroundInvoke method provides access to the context of the intercepted JAX-RS (REST) remote call. It allows to read and write the parameter values provided at the call site and provides read/write access to the RequestBuilder instance which has the URL, HTTP headers and parameters set.

Calling proceed executes the next interceptor in the chain or the actual remote call if all interceptors have already been executed. If access to the result of the (asynchronous) remote call is needed in the interceptor, one of the overloaded versions of proceed accepting a RemoteCallback has to be used instead.

The result of the remote call can be manipulated by calling RestCallContext.setResult().

Not calling proceed in the interceptor bypasses the actual remote call, passing RestCallContext.getResult() to the RemoteCallBack provided at the call site.

Wire Format

Errai's JSON format will be used to serialize/deserialize your custom types. See Marshalling for details.

Alternatively, a Jackson compatible JSON format can be used on the wire. See Configuration for details on how to enable Jackson marshalling.

Configuration

Configuring the default root path of JAX-RS endpoints

All paths specified using the @Path annotation on JAX-RS interfaces are by definition relative paths. Therefore, by default, it is assumed that the JAX-RS endpoints can be found at the specified paths relative to the GWT client application's context path.

To configure a relative or absolute root path, the following JavaScript variable can be set in either

the host HTML page

<script type="text/javascript">
  erraiJaxRsApplicationRoot = "/MyJaxRsEndpointPath";
</script>

or by using a JSNI method:

private native void setMyJaxRsAppRoot(String path) /*-{
  $wnd.erraiJaxRsApplicationRoot = path;
}-*/;

or by simply invoking:

RestClient.setApplicationRoot("/MyJaxRsEndpointPath");

The root path will be prepended to all paths specified on the JAX-RS interfaces. It serves as the base URL for all requests sent from the client.

Enabling Jackson marshalling

The following options are available for activating Jackson marshalling on the client. Note that this is a client-side configuration, the JAX-RS endpoint is assumed to already return a Jackson representation (Jackson is supported by all JAX-RS implementations). The errai-jaxrs-provider.jar does not have to be deployed on the server in this case!

<script type="text/javascript">
  erraiJaxRsJacksonMarshallingActive = true;
</script>

or by using a JSNI method:

private native void setJacksonMarshallingActive(boolean active) /*-{
  $wnd.erraiJaxRsJacksonMarshallingActive = active;
}-*/;

or by simply invoking:

RestClient.setJacksonMarshallingActive(true);
JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-10 12:34:54 UTC, last content change 2013-10-01 16:49:21 UTC.